home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume25 / trn / part09 < prev    next >
Encoding:
Internet Message Format  |  1991-12-02  |  57.5 KB

  1. Subject:  v25i012:  trn 2.0 - threaded newsreader based on rn 4.4, Part09/13
  2. Newsgroups: comp.sources.unix
  3. Approved: vixie@pa.dec.com
  4.  
  5. Submitted-by: davison@borland.com (Wayne Davison)
  6. Posting-number: Volume 25, Issue 12
  7. Archive-name: trn/part09
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line, then unpack
  11. # it by saving it into a file and typing "sh file".  To overwrite existing
  12. # files, type "sh file -c".  You can also feed this as standard input via
  13. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  14. # will see the following message at the end:
  15. #        "End of archive 9 (of 13)."
  16. # Contents:  intrp.c rcstuff.c
  17. # Wrapped by vixie@cognition.pa.dec.com on Tue Dec  3 16:34:55 1991
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. if test -f 'intrp.c' -a "${1}" != "-c" ; then 
  20.   echo shar: Will not clobber existing file \"'intrp.c'\"
  21. else
  22. echo shar: Extracting \"'intrp.c'\" \(27010 characters\)
  23. sed "s/^X//" >'intrp.c' <<'END_OF_FILE'
  24. X/* $Id: intrp.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
  25. X *
  26. X * $Log: intrp.c,v $
  27. X * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  28. X * Patchlevel 2 changes
  29. X *
  30. X * Revision 4.4  1991/09/09  20:18:23  sob
  31. X * release 4.4
  32. X *
  33. X *
  34. X * 
  35. X */
  36. X/* This software is Copyright 1991 by Stan Barber. 
  37. X *
  38. X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  39. X * use this software as long as: there is no monetary profit gained
  40. X * specifically from the use or reproduction of this software, it is not
  41. X * sold, rented, traded or otherwise marketed, and this copyright notice is
  42. X * included prominently in any copy made. 
  43. X *
  44. X * The author make no claims as to the fitness or correctness of this software
  45. X * for any use whatsoever, and it is provided as is. Any use of this software
  46. X * is at the user's own risk. 
  47. X */
  48. X#include "EXTERN.h"
  49. X#include "common.h"
  50. X#include "util.h"
  51. X#include "search.h"
  52. X#include "head.h"
  53. X#include "rn.h"
  54. X#include "artsrch.h"
  55. X#include "ng.h"
  56. X#include "respond.h"
  57. X#include "rcstuff.h"
  58. X#include "bits.h"
  59. X#include "artio.h"
  60. X#include "term.h"
  61. X#include "final.h"
  62. X#ifdef USETHREADS
  63. X#include "threads.h"
  64. X#include "rthreads.h"
  65. X#endif
  66. X#include "INTERN.h"
  67. X#include "intrp.h"
  68. X
  69. static char * regexp_specials = "^$.*[\\/?";
  70. X
  71. char orgname[] = ORGNAME;
  72. X
  73. X/* name of this site */
  74. X#ifdef GETHOSTNAME
  75. X    char *hostname;
  76. X#   undef SITENAME
  77. X#   define SITENAME hostname
  78. X#else /* !GETHOSTNAME */
  79. X#   ifdef DOUNAME
  80. X#    include <sys/utsname.h>
  81. X    struct utsname utsn;
  82. X#    undef SITENAME
  83. X#    define SITENAME utsn.nodename
  84. X#   else /* !DOUNAME */
  85. X#    ifdef PHOSTNAME
  86. X        char *hostname;
  87. X#        undef SITENAME
  88. X#        define SITENAME hostname
  89. X#    else /* !PHOSTNAME */
  90. X#        ifdef WHOAMI
  91. X#        undef SITENAME
  92. X#        define SITENAME sysname
  93. X#        endif /* WHOAMI */
  94. X#    endif /* PHOSTNAME */
  95. X#   endif /* DOUNAME */
  96. X#endif /* GETHOSTNAME */
  97. X
  98. X#ifdef TILDENAME
  99. static char *tildename = Nullch;
  100. static char *tildedir = Nullch;
  101. X#endif
  102. X
  103. char *realname INIT(Nullch);    /* real name of sender from /etc/passwd */
  104. X
  105. X#ifdef CONDSUB
  106. char *skipinterp ANSI((char *,char *));
  107. X#endif
  108. X
  109. static void abort_interp ANSI((void));
  110. X
  111. void
  112. intrp_init(tcbuf)
  113. char *tcbuf;
  114. X{
  115. X    char *getlogin();
  116. X
  117. X    spool = savestr(filexp(SPOOL));    /* usually /usr/spool/news */
  118. X#ifdef USETHREADS
  119. X    mtlib = savestr(filexp(MTLIB));
  120. X    threaddir = savestr(filexp(THREAD_DIR));
  121. X#endif
  122. X    
  123. X    /* get environmental stuff */
  124. X
  125. X#ifdef NEWSADMIN
  126. X    {
  127. X#ifdef GETPWENT
  128. X    struct passwd *getpwnam();
  129. X    struct passwd *pwd = getpwnam(NEWSADMIN);
  130. X
  131. X    if (pwd != NULL)
  132. X        newsuid = pwd->pw_uid;
  133. X#else
  134. X#ifdef TILDENAME
  135. X    char tildenews[2+sizeof NEWSADMIN];
  136. X    strcpy(tildenews, "~");
  137. X    strcat(tildenews, NEWSADMIN);
  138. X    (void) filexp(tildenews);
  139. X#else
  140. X    #error "At least one of GETPWENT or TILDENAME needed to get NEWSADMIN"
  141. X#endif  /* TILDENAME */
  142. X#endif    /* GETPWENT */
  143. X    }
  144. X#endif    /* NEWSADMIN */
  145. X    /* get home directory */
  146. X
  147. X    homedir = getenv("HOME");
  148. X    if (homedir == Nullch)
  149. X    homedir = getenv("LOGDIR");
  150. X
  151. X    dotdir = getval("DOTDIR",homedir);
  152. X
  153. X    /* get login name */
  154. X
  155. X    logname = getenv("USER");
  156. X    if (logname == Nullch)
  157. X    logname = getenv("LOGNAME");
  158. X#ifdef GETLOGIN
  159. X    if (logname == Nullch)
  160. X    logname = savestr(getlogin());
  161. X#endif
  162. X
  163. X#ifdef NEWSADMIN
  164. X    /* if this is the news admin than load his UID into newsuid */
  165. X
  166. X    if ( strEQ(logname,NEWSADMIN) )
  167. X    newsuid = getuid();
  168. X#endif
  169. X
  170. X    if (checkflag)            /* that getwd below takes ~1/3 sec. */
  171. X    return;                /* and we do not need it for -c */
  172. X    getwd(tcbuf);            /* find working directory name */
  173. X    origdir = savestr(tcbuf);        /* and remember it */
  174. X
  175. X    /* get the real name of the person (%N) */
  176. X    /* Must be done after logname is read in because BERKNAMES uses that */
  177. X
  178. X    strcpy(tcbuf,getrealname((long)getuid()));
  179. X    realname = savestr(tcbuf);
  180. X
  181. X    /* name of header file (%h) */
  182. X
  183. X    headname = savestr(filexp(HEADNAME));
  184. X
  185. X    /* name of this site (%H) */
  186. X
  187. X#ifdef HOSTFILE
  188. X    if ((tmpfp = fopen(HOSTFILE,"r")) == NULL) {
  189. X    hostname = "unknown";
  190. X    printf("Warning: Couldn't open %s to determine hostname!\n", HOSTFILE); 
  191. X    } else {
  192. X    fgets(buf, sizeof(buf), tmpfp);
  193. X    buf[strlen(buf)-1] = 0;
  194. X    hostname = savestr(buf);
  195. X    fclose(tmpfp);
  196. X    }
  197. X#else
  198. X#ifdef GETHOSTNAME
  199. X    gethostname(buf,sizeof buf);
  200. X    hostname = savestr(buf);
  201. X#else
  202. X#ifdef DOUNAME
  203. X    /* get sysname */
  204. X    uname(&utsn);
  205. X#else
  206. X#ifdef PHOSTNAME
  207. X    {
  208. X    FILE *popen();
  209. X    FILE *pipefp = popen(PHOSTNAME,"r");
  210. X    
  211. X    if (pipefp == Nullfp) {
  212. X        printf("Can't find hostname\n");
  213. X        sig_catcher(0);
  214. X    }
  215. X    fgets(buf,sizeof buf,pipefp);
  216. X    buf[strlen(buf)-1] = '\0';    /* wipe out newline */
  217. X    hostname = savestr(buf);
  218. X    pclose(pipefp);
  219. X    }
  220. X#endif    /* PHOSTNAME */
  221. X#endif    /* DOUNAME */
  222. X#endif    /* GETHOSTNAME */
  223. X#endif    /* HOSTFILE */
  224. X    if (index(SITENAME,'.') == NULL) {
  225. X    sprintf(buf, "%s.%s", SITENAME, OURDOMAIN);
  226. X    sitename = savestr(buf);
  227. X    } else
  228. X    sitename = savestr(SITENAME);
  229. X}
  230. X
  231. X/* expand filename via %, ~, and $ interpretation */
  232. X/* returns pointer to static area */
  233. X/* Note that there is a 1-deep cache of ~name interpretation */
  234. X
  235. char *
  236. filexp(s)
  237. register char *s;
  238. X{
  239. X    static char filename[CBUFLEN];
  240. X    char scrbuf[CBUFLEN];
  241. X    register char *d;
  242. X
  243. X#ifdef DEBUGGING
  244. X    if (debug & DEB_FILEXP)
  245. X    printf("< %s\n",s) FLUSH;
  246. X#endif
  247. X    interp(filename, (sizeof filename), s);    
  248. X                    /* interpret any % escapes */
  249. X#ifdef DEBUGGING
  250. X    if (debug & DEB_FILEXP)
  251. X    printf("%% %s\n",filename) FLUSH;
  252. X#endif
  253. X    s = filename;
  254. X    if (*s == '~') {    /* does destination start with ~? */
  255. X    if (!*(++s) || *s == '/') {
  256. X        sprintf(scrbuf,"%s%s",homedir,s);
  257. X                /* swap $HOME for it */
  258. X#ifdef DEBUGGING
  259. X    if (debug & DEB_FILEXP)
  260. X    printf("~ %s\n",scrbuf) FLUSH;
  261. X#endif
  262. X        strcpy(filename,scrbuf);
  263. X    }
  264. X    else {
  265. X#ifdef TILDENAME
  266. X        for (d=scrbuf; isalnum(*s); s++,d++)
  267. X        *d = *s;
  268. X        *d = '\0';
  269. X        if (tildedir && strEQ(tildename,scrbuf)) {
  270. X        strcpy(scrbuf,tildedir);
  271. X        strcat(scrbuf, s);
  272. X        strcpy(filename, scrbuf);
  273. X#ifdef DEBUGGING
  274. X        if (debug & DEB_FILEXP)
  275. X            printf("r %s %s\n",tildename,tildedir) FLUSH;
  276. X#endif
  277. X        }
  278. X        else {
  279. X        if (tildename) {
  280. X            free(tildename);
  281. X            free(tildedir);
  282. X        }
  283. X        tildedir = Nullch;
  284. X        tildename = savestr(scrbuf);
  285. X#ifdef GETPWENT        /* getpwnam() is not the paragon of efficiency */
  286. X        {
  287. X#ifdef notdef
  288. X            struct passwd *getpwnam ANSI((char*));
  289. X#endif
  290. X            struct passwd *pwd = getpwnam(tildename);
  291. X            if ( pwd == NULL){
  292. X            printf("%s is an unknown user. Using default.\n",tildename) FLUSH;
  293. X            return(Nullch);
  294. X            }
  295. X            sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
  296. X            tildedir = savestr(pwd->pw_dir);
  297. X            strcpy(filename,scrbuf);
  298. X            endpwent();
  299. X        }
  300. X#else            /* this will run faster, and is less D space */
  301. X        {    /* just be sure LOGDIRFIELD is correct */
  302. X            FILE *pfp = fopen("/etc/passwd","r");
  303. X            char tmpbuf[512];
  304. X            int i;
  305. X            
  306. X            if (pfp == Nullfp) {
  307. X            printf(cantopen,"passwd") FLUSH;
  308. X            sig_catcher(0);
  309. X            }
  310. X            while (fgets(tmpbuf,512,pfp) != Nullch) {
  311. X            d = cpytill(scrbuf,tmpbuf,':');
  312. X#ifdef DEBUGGING
  313. X            if (debug & DEB_FILEXP)
  314. X                printf("p %s\n",tmpbuf) FLUSH;
  315. X#endif
  316. X            if (strEQ(scrbuf,tildename)) {
  317. X                for (i=LOGDIRFIELD-2; i; i--) {
  318. X                if (d)
  319. X                    d = index(d+1,':');
  320. X                }
  321. X                if (d) {
  322. X                cpytill(scrbuf,d+1,':');
  323. X                tildedir = savestr(scrbuf);
  324. X                strcat(scrbuf,s);
  325. X                strcpy(filename,scrbuf);
  326. X                }
  327. X                break;
  328. X            }
  329. X            }
  330. X            fclose(pfp);
  331. X        }
  332. X#endif
  333. X        }
  334. X#else /* !TILDENAME */
  335. X#ifdef VERBOSE
  336. X        IF(verbose)
  337. X        fputs("~loginname not implemented.\n",stdout) FLUSH;
  338. X        ELSE
  339. X#endif
  340. X#ifdef TERSE
  341. X        fputs("~login not impl.\n",stdout) FLUSH;
  342. X#endif
  343. X#endif
  344. X    }
  345. X    }
  346. X    else if (*s == '$') {    /* starts with some env variable? */
  347. X    d = scrbuf;
  348. X    *d++ = '%';
  349. X    if (s[1] == '{')
  350. X        strcpy(d,s+2);
  351. X    else {
  352. X        *d++ = '{';
  353. X        for (s++; isalnum(*s); s++) *d++ = *s;
  354. X                /* skip over token */
  355. X        *d++ = '}';
  356. X        strcpy(d,s);
  357. X    }
  358. X#ifdef DEBUGGING
  359. X    if (debug & DEB_FILEXP)
  360. X        printf("$ %s\n",scrbuf) FLUSH;
  361. X#endif
  362. X    interp(filename, (sizeof filename), scrbuf);
  363. X                    /* this might do some extra '%'s but */
  364. X                    /* that is how the Mercedes Benz */
  365. X    }
  366. X#ifdef DEBUGGING
  367. X    if (debug & DEB_FILEXP)
  368. X    printf("> %s\n",filename) FLUSH;
  369. X#endif
  370. X    return filename;
  371. X}
  372. X
  373. X#ifdef CONDSUB
  374. X/* skip interpolations */
  375. X
  376. char *
  377. skipinterp(pattern,stoppers)
  378. register char *pattern;
  379. char *stoppers;
  380. X{
  381. X
  382. X    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
  383. X#ifdef DEBUGGING
  384. X    if (debug & 8)
  385. X        printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern);
  386. X#endif
  387. X    if (*pattern == '%' && pattern[1]) {
  388. X        switch (*++pattern) {
  389. X        case '{':
  390. X        for (pattern++; *pattern && *pattern != '}'; pattern++)
  391. X            if (*pattern == '\\')
  392. X            pattern++;
  393. X        break;
  394. X        case '[':
  395. X        for (pattern++; *pattern && *pattern != ']'; pattern++)
  396. X            if (*pattern == '\\')
  397. X            pattern++;
  398. X        break;
  399. X#ifdef CONDSUB
  400. X        case '(': {
  401. X        pattern = skipinterp(pattern+1,"!=");
  402. X        if (!*pattern)
  403. X            goto getout;
  404. X        for (pattern++; *pattern && *pattern != '?'; pattern++)
  405. X            if (*pattern == '\\')
  406. X            pattern++;
  407. X        if (!*pattern)
  408. X            goto getout;
  409. X        pattern = skipinterp(pattern+1,":)");
  410. X        if (*pattern == ':')
  411. X            pattern = skipinterp(pattern+1,")");
  412. X        break;
  413. X        }
  414. X#endif
  415. X#ifdef BACKTICK
  416. X        case '`': {
  417. X        pattern = skipinterp(pattern+1,"`");
  418. X        break;
  419. X        }
  420. X#endif
  421. X#ifdef PROMPTTTY
  422. X        case '"':
  423. X        pattern = skipinterp(pattern+1,"\"");
  424. X        break;
  425. X#endif
  426. X        default:
  427. X        break;
  428. X        }
  429. X        pattern++;
  430. X    }
  431. X    else {
  432. X        if (*pattern == '^' && pattern[1])
  433. X        pattern += 2;
  434. X        else if (*pattern == '\\' && pattern[1])
  435. X        pattern += 2;
  436. X        else
  437. X        pattern++;
  438. X    }
  439. X    }
  440. getout:
  441. X    return pattern;            /* where we left off */
  442. X}
  443. X#endif
  444. X
  445. X/* interpret interpolations */
  446. X
  447. char *
  448. dointerp(dest,destsize,pattern,stoppers)
  449. register char *dest;
  450. register int destsize;
  451. register char *pattern;
  452. char *stoppers;
  453. X{
  454. X    char *subj_buf = Nullch;
  455. X    char *ngs_buf = Nullch;
  456. X    char *refs_buf = Nullch;
  457. X    char *artid_buf = Nullch;
  458. X    char *reply_buf = Nullch;
  459. X    char *from_buf = Nullch;
  460. X    char *path_buf = Nullch;
  461. X    char *follow_buf = Nullch;
  462. X    char *dist_buf = Nullch;
  463. X    char *line_buf = Nullch;
  464. X    register char *s, *h;
  465. X    register int i;
  466. X    char scrbuf[512];
  467. X    bool upper = FALSE;
  468. X    bool lastcomp = FALSE;
  469. X    bool re_quote = FALSE;
  470. X    int metabit = 0;
  471. X
  472. X    while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
  473. X#ifdef DEBUGGING
  474. X    if (debug & 8)
  475. X        printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern);
  476. X#endif
  477. X    if (*pattern == '%' && pattern[1]) {
  478. X        upper = FALSE;
  479. X        lastcomp = FALSE;
  480. X        re_quote = FALSE;
  481. X        for (s=Nullch; !s; ) {
  482. X        switch (*++pattern) {
  483. X        case '^':
  484. X            upper = TRUE;
  485. X            break;
  486. X        case '_':
  487. X            lastcomp = TRUE;
  488. X            break;
  489. X        case '\\':
  490. X            re_quote = TRUE;
  491. X            break;
  492. X        case '/':
  493. X#ifdef ARTSRCH
  494. X            s = scrbuf;
  495. X            if (!index("/?g",pattern[-2]))
  496. X            *s++ = '/';
  497. X            strcpy(s,lastpat);
  498. X            s += strlen(s);
  499. X            if (pattern[-2] != 'g') {
  500. X            if (index("/?",pattern[-2]))
  501. X                *s++ = pattern[-2];
  502. X            else
  503. X                *s++ = '/';
  504. X            if (art_howmuch == 1)
  505. X                *s++ = 'h';
  506. X            else if (art_howmuch == 2)
  507. X                *s++ = 'a';
  508. X            if (art_doread)
  509. X                *s++ = 'r';
  510. X            }
  511. X            *s = '\0';
  512. X            s = scrbuf;
  513. X#else
  514. X            s = nullstr;
  515. X#endif
  516. X            break;
  517. X        case '{':
  518. X            pattern = cpytill(scrbuf,pattern+1,'}');
  519. X            if (s = index(scrbuf,'-'))
  520. X            *s++ = '\0';
  521. X            else
  522. X            s = nullstr;
  523. X            s = getval(scrbuf,s);
  524. X            break;
  525. X        case '[':
  526. X            pattern = cpytill(scrbuf,pattern+1,']');
  527. X            i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
  528. X            if (line_buf)
  529. X            free(line_buf);
  530. X            s = line_buf = fetchlines(art,i);
  531. X            break;
  532. X#ifdef CONDSUB
  533. X        case '(': {
  534. X            COMPEX *oldbra_compex = bra_compex;
  535. X            COMPEX cond_compex;
  536. X            char rch;
  537. X            bool matched;
  538. X            
  539. X            init_compex(&cond_compex);
  540. X            pattern = dointerp(dest,destsize,pattern+1,"!=");
  541. X            rch = *pattern;
  542. X            if (rch == '!')
  543. X            pattern++;
  544. X            if (*pattern != '=')
  545. X            goto getout;
  546. X            pattern = cpytill(scrbuf,pattern+1,'?');
  547. X            if (!*pattern)
  548. X            goto getout;
  549. X            if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
  550. X            printf("%s: %s\n",scrbuf,s) FLUSH;
  551. X            pattern += strlen(pattern);
  552. X            goto getout;
  553. X            }
  554. X            matched = (execute(&cond_compex,dest) != Nullch);
  555. X            if (cond_compex.nbra)    /* were there brackets? */
  556. X            bra_compex = &cond_compex;
  557. X            if (matched==(rch == '=')) {
  558. X            pattern = dointerp(dest,destsize,pattern+1,":)");
  559. X            if (*pattern == ':')
  560. X                pattern = skipinterp(pattern+1,")");
  561. X            }
  562. X            else {
  563. X            pattern = skipinterp(pattern+1,":)");
  564. X            if (*pattern == ':')
  565. X                pattern++;
  566. X            pattern = dointerp(dest,destsize,pattern,")");
  567. X            }
  568. X            s = dest;
  569. X            bra_compex = oldbra_compex;
  570. X            free_compex(&cond_compex);
  571. X            break;
  572. X        }
  573. X#endif
  574. X#ifdef BACKTICK
  575. X        case '`': {
  576. X            FILE *pipefp, *popen();
  577. X
  578. X            pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
  579. X            pipefp = popen(scrbuf,"r");
  580. X            if (pipefp != Nullfp) {
  581. X            int len;
  582. X
  583. X            len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
  584. X                pipefp);
  585. X            scrbuf[len] = '\0';
  586. X            pclose(pipefp);
  587. X            }
  588. X            else {
  589. X            printf("\nCan't run %s\n",scrbuf);
  590. X            *scrbuf = '\0';
  591. X            }
  592. X            for (s=scrbuf; *s; s++) {
  593. X            if (*s == '\n') {
  594. X                if (s[1])
  595. X                *s = ' ';
  596. X                else
  597. X                *s = '\0';
  598. X            }
  599. X            }
  600. X            s = scrbuf;
  601. X            break;
  602. X        }
  603. X#endif
  604. X#ifdef PROMPTTTY
  605. X        case '"':
  606. X            pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
  607. X            fputs(scrbuf,stdout) FLUSH;
  608. X            resetty();
  609. X            gets(scrbuf);
  610. X            noecho();
  611. X            crmode();
  612. X            s = scrbuf;
  613. X            break;
  614. X#endif
  615. X        case '~':
  616. X            s = homedir;
  617. X            break;
  618. X        case '.':
  619. X            s = dotdir;
  620. X            break;
  621. X        case '$':
  622. X            s = scrbuf;
  623. X            sprintf(s,"%d",getpid());
  624. X            break;
  625. X        case '#':
  626. X            s = scrbuf;
  627. X            sprintf(s,"%d",perform_cnt);
  628. X            break;
  629. X        case '0': case '1': case '2': case '3': case '4':
  630. X        case '5': case '6': case '7': case '8': case '9':
  631. X#ifdef CONDSUB
  632. X            s = getbracket(bra_compex,*pattern - '0');
  633. X#else
  634. X            s = nullstr;
  635. X#endif
  636. X            break;
  637. X        case 'a':
  638. X            s = scrbuf;
  639. X            sprintf(s,"%ld",(long)art);
  640. X            break;
  641. X        case 'A':
  642. X#ifdef LINKART
  643. X            s = linkartname;    /* so Eunice people get right file */
  644. X#else
  645. X            s = scrbuf;
  646. X#ifdef SERVER
  647. X            sprintf(s,"%s/rrn%ld.%d",spool,(long)art,getpid());
  648. X#else
  649. X            sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
  650. X#endif
  651. X#endif
  652. X            break;
  653. X        case 'b':
  654. X            s = savedest;
  655. X            break;
  656. X        case 'B':
  657. X            s = scrbuf;
  658. X            sprintf(s,"%ld",(long)savefrom);
  659. X            break;
  660. X        case 'c':
  661. X            s = ngdir;
  662. X            break;
  663. X        case 'C':
  664. X            s = ngname;
  665. X            break;
  666. X        case 'd':
  667. X            s = scrbuf;
  668. X            sprintf(s,"%s/%s",spool,ngdir);
  669. X            break;
  670. X        case 'D':
  671. X            s = dist_buf = fetchlines(art,DIST_LINE);
  672. X            break;
  673. X        case 'e':
  674. X            s = (extractprog ? extractprog : "-");
  675. X            break;
  676. X        case 'E':
  677. X            s = extractdest;
  678. X            break;
  679. X        case 'f':            /* from line */
  680. X#ifdef ASYNC_PARSE
  681. X            parse_maybe(art);
  682. X#endif
  683. X            if (htype[REPLY_LINE].ht_minpos >= 0) {
  684. X                        /* was there a reply line? */
  685. X            if (!(s=reply_buf))
  686. X                s = reply_buf = fetchlines(art,REPLY_LINE);
  687. X            }
  688. X            else if (!(s = from_buf))
  689. X            s = from_buf = fetchlines(art,FROM_LINE);
  690. X            break;
  691. X        case 'F':
  692. X#ifdef ASYNC_PARSE
  693. X            parse_maybe(art);
  694. X#endif
  695. X            if (htype[FOLLOW_LINE].ht_minpos >= 0)
  696. X                    /* is there a Followup-To line? */
  697. X            s = follow_buf = fetchlines(art,FOLLOW_LINE);
  698. X            else 
  699. X            s = ngs_buf = fetchlines(art,NGS_LINE);
  700. X            break;
  701. X        case 'h':            /* header file name */
  702. X            s = headname;
  703. X            break;
  704. X        case 'H':            /* host name */
  705. X            s = sitename;
  706. X            break;
  707. X        case 'i':
  708. X            if (!(s=artid_buf))
  709. X            s = artid_buf = fetchlines(art,MESSID_LINE);
  710. X            if (*s && *s != '<') {
  711. X            sprintf(scrbuf,"<%s>",artid_buf);
  712. X            s = scrbuf;
  713. X            }
  714. X            break;
  715. X        case 'I':            /* ref article indicator */
  716. X            s = scrbuf;
  717. X            sprintf(scrbuf,"'%s'",indstr);
  718. X            break;
  719. X        case 'l':            /* rn library */
  720. X#ifdef NEWSADMIN
  721. X            s = newsadmin;
  722. X#else
  723. X            s = "???";
  724. X#endif
  725. X            break;
  726. X        case 'L':            /* login id */
  727. X            s = logname;
  728. X            break;
  729. X        case 'm':        /* current mode */
  730. X            s = scrbuf;
  731. X            *s = mode;
  732. X            s[1] = '\0';
  733. X            break;
  734. X        case 'M':
  735. X#ifdef DELAYMARK
  736. X            sprintf(scrbuf,"%ld",(long)dmcount);
  737. X            s = scrbuf;
  738. X#else
  739. X            s = nullstr;
  740. X#endif
  741. X            break;
  742. X        case 'n':            /* newsgroups */
  743. X            s = ngs_buf = fetchlines(art,NGS_LINE);
  744. X            break;
  745. X        case 'N':            /* full name */
  746. X            s = getval("NAME",realname);
  747. X            break;
  748. X        case 'o':            /* organization */
  749. X#ifdef IGNOREORG
  750. X            s = getval("NEWSORG",orgname); 
  751. X#else
  752. X            s = getenv("NEWSORG");
  753. X            if (s == Nullch) 
  754. X            s = getval("ORGANIZATION",orgname); 
  755. X#endif
  756. X#ifdef ORGFILE
  757. X            if (*s == '/') {
  758. X            FILE *ofp = fopen(s,"r");
  759. X
  760. X            if (ofp) {
  761. X                fgets(scrbuf,sizeof scrbuf,ofp);
  762. X                fclose(ofp);
  763. X                s = scrbuf+strlen(scrbuf)-1;
  764. X                if (*s == '\n')
  765. X                *s = '\0';
  766. X                s = scrbuf;
  767. X            }
  768. X            }
  769. X#endif
  770. X            break;
  771. X        case 'O':
  772. X            s = origdir;
  773. X            break;
  774. X        case 'p':
  775. X            s = cwd;
  776. X            break;
  777. X        case 'P':
  778. X            s = spool;
  779. X            break;
  780. X        case 'r':
  781. X#ifdef ASYNC_PARSE
  782. X            parse_maybe(art);
  783. X#endif
  784. X            if (htype[REFS_LINE].ht_minpos >= 0) {
  785. X            refs_buf = fetchlines(art,REFS_LINE);
  786. X            refscpy(scrbuf,(sizeof scrbuf),refs_buf);
  787. X            }
  788. X            else
  789. X            *scrbuf = '\0';
  790. X            s = rindex(scrbuf,'<');
  791. X            break;
  792. X        case 'R':
  793. X#ifdef ASYNC_PARSE
  794. X            parse_maybe(art);
  795. X#endif
  796. X            if (htype[REFS_LINE].ht_minpos >= 0) {
  797. X            refs_buf = fetchlines(art,REFS_LINE);
  798. X            refscpy(scrbuf,(sizeof scrbuf),refs_buf);
  799. X            /* no more than 3 prior references allowed,
  800. X            ** including the one concatenated below */
  801. X            if ((s = rindex(scrbuf,'<')) > scrbuf) {
  802. X                *s = '\0';
  803. X                h = rindex(scrbuf,'<');
  804. X                *s = '<';
  805. X                if (h > scrbuf)
  806. X                strcpy(scrbuf,h);
  807. X            }
  808. X            }
  809. X            else
  810. X            *scrbuf = '\0';
  811. X            if (!artid_buf)
  812. X            artid_buf = fetchlines(art,MESSID_LINE);
  813. X            if (artid_buf[0] == '<')
  814. X            safecat(scrbuf,artid_buf,sizeof(scrbuf));
  815. X            else if (artid_buf[0]) {
  816. X            char tmpbuf[64];
  817. X    
  818. X            sprintf(tmpbuf,"<%s>",artid_buf);
  819. X            safecat(scrbuf,tmpbuf,sizeof(scrbuf));
  820. X            }
  821. X            s = scrbuf;
  822. X            break;
  823. X        case 's':
  824. X            if (!(s=subj_buf))
  825. X            s = subj_buf = fetchsubj(art,TRUE,TRUE);
  826. X                        /* get subject handy */
  827. X            while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
  828. X                        /* skip extra Re: */
  829. X            s += 3;
  830. X            if (*s == ' ')
  831. X                s++;
  832. X            }
  833. X            if (h = instr(s,"- (nf", TRUE))
  834. X            *h = '\0';
  835. X            break;
  836. X        case 'S':
  837. X            if (!(s=subj_buf))
  838. X            s = subj_buf = fetchsubj(art,TRUE,TRUE);
  839. X                        /* get subject handy */
  840. X            if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
  841. X                        /* skip extra Re: */
  842. X            s += 3;
  843. X            if (*s == ' ')
  844. X                s++;
  845. X            }
  846. X            break;
  847. X        case 't':
  848. X        case 'T':
  849. X#ifdef ASYNC_PARSE
  850. X            parse_maybe(art);
  851. X#endif
  852. X            if (htype[REPLY_LINE].ht_minpos >= 0) {
  853. X                    /* was there a reply line? */
  854. X            if (!(s=reply_buf))
  855. X                s = reply_buf = fetchlines(art,REPLY_LINE);
  856. X            }
  857. X            else if (!(s = from_buf))
  858. X            s = from_buf = fetchlines(art,FROM_LINE);
  859. X            if (*pattern == 'T') {
  860. X            if (htype[PATH_LINE].ht_minpos >= 0) {
  861. X                    /* should we substitute path? */
  862. X                s = path_buf = fetchlines(art,PATH_LINE);
  863. X            }
  864. X            i = strlen(sitename);
  865. X            if (strnEQ(sitename,s,i) && s[i] == '!')
  866. X                s += i + 1;
  867. X            }
  868. X            if ((h=index(s,'(')) != Nullch)
  869. X                        /* strip garbage from end */
  870. X            *(h-1) = '\0';
  871. X            else if ((h=index(s,'<')) != Nullch) {
  872. X                        /* or perhaps from beginning */
  873. X            s = h+1;
  874. X            if ((h=index(s,'>')) != Nullch)
  875. X                *h = '\0';
  876. X            }
  877. X            break;
  878. X        case 'u':
  879. X            sprintf(scrbuf,"%ld",(long)toread[ng]);
  880. X            s = scrbuf;
  881. X            break;
  882. X        case 'U': {
  883. X            int unseen;
  884. X
  885. X            unseen = (art <= lastart) && !was_read(art);
  886. X#ifdef USETHREADS
  887. X            if (selected_root_cnt) {
  888. X            int selected;
  889. X
  890. X            selected = curr_p_art
  891. X                && (selected_roots[curr_p_art->root] & 1);
  892. X            sprintf(scrbuf,"%ld",
  893. X                (long)selected_count - (selected && unseen));
  894. X            }
  895. X            else
  896. X            sprintf(scrbuf,"%ld",(long)toread[ng]-unthreaded
  897. X                        -unseen);
  898. X#else
  899. X            sprintf(scrbuf,"%ld",(long)toread[ng]-unseen);
  900. X#endif
  901. X            s = scrbuf;
  902. X            break;
  903. X        }
  904. X#ifdef USETHREADS
  905. X        case 'v': {
  906. X            int selected, unseen;
  907. X
  908. X            selected = curr_p_art
  909. X                && (selected_roots[curr_p_art->root] & 1);
  910. X            unseen = (art <= lastart) && !was_read(art);
  911. X            sprintf(scrbuf,"%ld",(long)toread[ng] - selected_count
  912. X                     - unthreaded - (!selected && unseen));
  913. X            s = scrbuf;
  914. X            break;
  915. X        }
  916. X        case 'w':
  917. X            s = mtlib;
  918. X            break;
  919. X        case 'W':
  920. X            s = threaddir;
  921. X            break;
  922. X#endif
  923. X        case 'x':            /* news library */
  924. X            s = lib;
  925. X            break;
  926. X        case 'X':            /* rn library */
  927. X            s = rnlib;
  928. X            break;
  929. X        case 'z':
  930. X#ifdef LINKART
  931. X            s = linkartname;    /* so Eunice people get right file */
  932. X#else
  933. X            s = scrbuf;
  934. X            sprintf(s,"%ld",(long)art);
  935. X#endif
  936. X            if (stat(s,&filestat) < 0)
  937. X            filestat.st_size = 0L;
  938. X            sprintf(scrbuf,"%5ld",(long)filestat.st_size);
  939. X            s = scrbuf;
  940. X            break;
  941. X#ifdef USETHREADS
  942. X        case 'Z':
  943. X            sprintf(scrbuf,"%ld",(long)selected_count);
  944. X            s = scrbuf;
  945. X            break;
  946. X#endif
  947. X        default:
  948. X            if (--destsize <= 0)
  949. X            abort_interp();
  950. X            *dest++ = *pattern | metabit;
  951. X            s = nullstr;
  952. X            break;
  953. X        }
  954. X        }
  955. X        if (!s)
  956. X        s = nullstr;
  957. X        pattern++;
  958. X        if (upper || lastcomp) {
  959. X        char *t;
  960. X
  961. X        if (s != scrbuf) {
  962. X            safecpy(scrbuf,s,(sizeof scrbuf));
  963. X            s = scrbuf;
  964. X        }
  965. X        if (upper || !(t=rindex(s,'/')))
  966. X            t = s;
  967. X        while (*t && !isalpha(*t))
  968. X            t++;
  969. X        if (islower(*t))
  970. X            *t = toupper(*t);
  971. X        }
  972. X        /* Do we have room left? */
  973. X        i = strlen(s);
  974. X        if (destsize <= i)
  975. X        abort_interp();
  976. X        destsize -= i;    /* adjust the size now. */
  977. X
  978. X        /* A maze of twisty little conditions, all alike... */
  979. X        if (metabit) {
  980. X        /* set meta bit while copying. */
  981. X        i = metabit;        /* maybe get into register */
  982. X        if (s == dest) {
  983. X            while (*dest)
  984. X            *dest++ |= i;
  985. X        } else {
  986. X            while (*s)
  987. X            *dest++ = *s++ | i;
  988. X        }
  989. X        } else if (re_quote) {
  990. X        /* put a backslash before regexp specials while copying. */
  991. X        if (s == dest) {
  992. X            /* copy out so we can copy in. */
  993. X            safecpy(scrbuf, s, sizeof scrbuf);
  994. X            s = scrbuf;
  995. X            if (i > sizeof scrbuf)    /* we truncated, ack! */
  996. X            destsize += i - sizeof scrbuf;
  997. X        }
  998. X        while (*s) {
  999. X            if (index(regexp_specials, *s)) {
  1000. X            if (--destsize <= 0)
  1001. X                abort_interp();
  1002. X            *dest++ = '\\';
  1003. X            }
  1004. X            *dest++ = *s++;
  1005. X        }
  1006. X        } else {
  1007. X        /* straight copy. */
  1008. X        if (s == dest) {
  1009. X            dest += i;
  1010. X        } else {
  1011. X            while (*s)
  1012. X            *dest++ = *s++;
  1013. X        }
  1014. X        }
  1015. X    }
  1016. X    else {
  1017. X        if (--destsize <= 0)
  1018. X        abort_interp();
  1019. X        if (*pattern == '^' && pattern[1]) {
  1020. X        ++pattern;            /* skip uparrow */
  1021. X        i = *pattern;        /* get char into a register */
  1022. X        if (i == '?')
  1023. X            *dest++ = '\177' | metabit;
  1024. X        else if (i == '(') {
  1025. X            metabit = 0200;
  1026. X            destsize++;
  1027. X        }
  1028. X        else if (i == ')') {
  1029. X            metabit = 0;
  1030. X            destsize++;
  1031. X        }
  1032. X        else
  1033. X            *dest++ = i & 037 | metabit;
  1034. X        pattern++;
  1035. X        }
  1036. X        else if (*pattern == '\\' && pattern[1]) {
  1037. X        ++pattern;            /* skip backslash */
  1038. X        i = *pattern;        /* get char into a register */
  1039. X    
  1040. X        /* this used to be a switch but the if may save space */
  1041. X        
  1042. X        if (i >= '0' && i <= '7') {
  1043. X            i = 1;
  1044. X            while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
  1045. X            i <<= 3;
  1046. X            i += *pattern++ - '0';
  1047. X            }
  1048. X            *dest++ = i & 0377 | metabit;
  1049. X            --pattern;
  1050. X        }
  1051. X        else if (i == 'b')
  1052. X            *dest++ = '\b' | metabit;
  1053. X        else if (i == 'f')
  1054. X            *dest++ = '\f' | metabit;
  1055. X        else if (i == 'n')
  1056. X            *dest++ = '\n' | metabit;
  1057. X        else if (i == 'r')
  1058. X            *dest++ = '\r' | metabit;
  1059. X        else if (i == 't')
  1060. X            *dest++ = '\t' | metabit;
  1061. X        else
  1062. X            *dest++ = i | metabit;
  1063. X        pattern++;
  1064. X        }
  1065. X        else
  1066. X        *dest++ = *pattern++ | metabit;
  1067. X    }
  1068. X    }
  1069. X    *dest = '\0';
  1070. getout:
  1071. X    if (subj_buf != Nullch)    /* return any checked out storage */
  1072. X    free(subj_buf);
  1073. X    if (ngs_buf != Nullch)
  1074. X    free(ngs_buf);
  1075. X    if (refs_buf != Nullch)
  1076. X    free(refs_buf);
  1077. X    if (artid_buf != Nullch)
  1078. X    free(artid_buf);
  1079. X    if (reply_buf != Nullch)
  1080. X    free(reply_buf);
  1081. X    if (from_buf != Nullch)
  1082. X    free(from_buf);
  1083. X    if (path_buf != Nullch)
  1084. X    free(path_buf);
  1085. X    if (follow_buf != Nullch)
  1086. X    free(follow_buf);
  1087. X    if (dist_buf != Nullch)
  1088. X    free(dist_buf);
  1089. X    if (line_buf != Nullch)
  1090. X    free(line_buf);
  1091. X    return pattern;            /* where we left off */
  1092. X}
  1093. X
  1094. void
  1095. interp(dest,destsize,pattern)
  1096. char *dest;
  1097. int destsize;
  1098. char *pattern;
  1099. X{
  1100. X    dointerp(dest,destsize,pattern,Nullch);
  1101. X#ifdef DEBUGGING
  1102. X    if (debug & DEB_FILEXP)
  1103. X    fputs(dest,stdout);
  1104. X#endif
  1105. X}
  1106. X
  1107. X/* copy a references line, normalizing as we go */
  1108. X
  1109. void
  1110. refscpy(dest,destsize,src)
  1111. register char *dest, *src;
  1112. register int destsize;
  1113. X{
  1114. X    register char *dot, *at, *beg;
  1115. X    char tmpbuf[64];
  1116. X    
  1117. X    while (*src) {
  1118. X    if (*src != '<') {
  1119. X        if (--destsize <= 0)
  1120. X        break;
  1121. X        *dest++ = '<';
  1122. X        at = dot = Nullch;
  1123. X        beg = src;
  1124. X        while (*src && *src != ' ' && *src != ',') {
  1125. X        if (*src == '.')
  1126. X            dot = src;
  1127. X        else if (*src == '@')
  1128. X            at = src;
  1129. X        if (--destsize <= 0)
  1130. X            break;
  1131. X        *dest++ = *src++;
  1132. X        }
  1133. X        if (destsize <= 0)
  1134. X        break;
  1135. X        if (dot && !at) {
  1136. X        int len;
  1137. X
  1138. X        *dest = *dot++ = '\0';
  1139. X        sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
  1140. X        len = strlen(tmpbuf);
  1141. X        if (destsize > len) {
  1142. X            strcpy(dest,tmpbuf);
  1143. X            dest = dest + len;
  1144. X            destsize -= len;
  1145. X        }
  1146. X        }
  1147. X        if (--destsize <= 0)
  1148. X        break;
  1149. X        *dest++ = '>';
  1150. X    }
  1151. X    else {
  1152. X        while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ;
  1153. X        if (destsize <= 0)
  1154. X        break;
  1155. X    }
  1156. X    while (*src == ' ' || *src == ',') src++;
  1157. X    if (*src && --destsize > 0)
  1158. X        *dest++ = ' ';
  1159. X    }
  1160. X    *dest = '\0';
  1161. X} 
  1162. X
  1163. X/* get the person's real name from /etc/passwd */
  1164. X/* (string is overwritten, so it must be copied) */
  1165. X
  1166. char *
  1167. getrealname(uid)
  1168. long uid;
  1169. X{
  1170. X    char *s, *c;
  1171. X
  1172. X#ifdef PASSNAMES
  1173. X#ifdef GETPWENT
  1174. X#ifdef notdef
  1175. X    struct passwd *getpwuid ANSI((uid_t));
  1176. X#endif
  1177. X    struct passwd *pwd = getpwuid(uid);
  1178. X    
  1179. X    s = pwd->pw_gecos;
  1180. X#else
  1181. X    char tmpbuf[512];
  1182. X    int i;
  1183. X
  1184. X    getpw(uid, tmpbuf);
  1185. X    for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
  1186. X    if (s)
  1187. X        s = index(s,':')+1;
  1188. X    }
  1189. X    if (!s)
  1190. X    return nullstr;
  1191. X    cpytill(tmpbuf,s,':');
  1192. X    s = tmpbuf;
  1193. X#endif
  1194. X#ifdef BERKNAMES
  1195. X#ifdef BERKJUNK
  1196. X    while (*s && !isalnum(*s) && *s != '&') s++;
  1197. X#endif
  1198. X    if ((c = index(s, ',')) != Nullch)
  1199. X    *c = '\0';
  1200. X    if ((c = index(s, ';')) != Nullch)
  1201. X    *c = '\0';
  1202. X    s = cpytill(buf,s,'&');
  1203. X    if (*s == '&') {            /* whoever thought this one up was */
  1204. X    c = buf + strlen(buf);        /* in the middle of the night */
  1205. X    strcat(c,logname);        /* before the morning after */
  1206. X    strcat(c,s+1);
  1207. X    if (islower(*c))
  1208. X        *c = toupper(*c);        /* gack and double gack */
  1209. X    }
  1210. X#else
  1211. X    if ((c = index(s, '(')) != Nullch)
  1212. X    *c = '\0';
  1213. X    if ((c = index(s, '-')) != Nullch)
  1214. X    s = c;
  1215. X    strcpy(buf,tmpbuf);
  1216. X#endif
  1217. X#ifdef GETPWENT
  1218. X    endpwent();
  1219. X#endif
  1220. X    return buf;                /* return something static */
  1221. X#else
  1222. X    if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
  1223. X    fgets(buf,sizeof buf,tmpfp);
  1224. X    fclose(tmpfp);
  1225. X    buf[strlen(buf)-1] = '\0';
  1226. X    return buf;
  1227. X    }
  1228. X    return "PUT YOUR NAME HERE";
  1229. X#endif
  1230. X}
  1231. X
  1232. static void
  1233. abort_interp()
  1234. X{
  1235. X    fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
  1236. X    sig_catcher(0);
  1237. X}
  1238. X
  1239. X
  1240. END_OF_FILE
  1241. if test 27010 -ne `wc -c <'intrp.c'`; then
  1242.     echo shar: \"'intrp.c'\" unpacked with wrong size!
  1243. fi
  1244. # end of 'intrp.c'
  1245. fi
  1246. if test -f 'rcstuff.c' -a "${1}" != "-c" ; then 
  1247.   echo shar: Will not clobber existing file \"'rcstuff.c'\"
  1248. else
  1249. echo shar: Extracting \"'rcstuff.c'\" \(27693 characters\)
  1250. sed "s/^X//" >'rcstuff.c' <<'END_OF_FILE'
  1251. X/* $Id: rcstuff.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
  1252. X *
  1253. X * $Log: rcstuff.c,v $
  1254. X * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  1255. X * Patchlevel 2 changes
  1256. X *
  1257. X * Revision 4.4  1991/09/09  20:27:37  sob
  1258. X * release 4.4
  1259. X *
  1260. X *
  1261. X * 
  1262. X */
  1263. X/* This software is Copyright 1991 by Stan Barber. 
  1264. X *
  1265. X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  1266. X * use this software as long as: there is no monetary profit gained
  1267. X * specifically from the use or reproduction of this software, it is not
  1268. X * sold, rented, traded or otherwise marketed, and this copyright notice is
  1269. X * included prominently in any copy made. 
  1270. X *
  1271. X * The author make no claims as to the fitness or correctness of this software
  1272. X * for any use whatsoever, and it is provided as is. Any use of this software
  1273. X * is at the user's own risk. 
  1274. X */
  1275. X
  1276. X#include "EXTERN.h"
  1277. X#include "common.h"
  1278. X#include "util.h"
  1279. X#include "ngdata.h"
  1280. X#include "term.h"
  1281. X#include "final.h"
  1282. X#include "rn.h"
  1283. X#include "intrp.h"
  1284. X#include "only.h"
  1285. X#include "rcln.h"
  1286. X#ifdef SERVER
  1287. X#include "server.h"
  1288. X#endif
  1289. X#include "autosub.h"
  1290. X#include "INTERN.h"
  1291. X#include "rcstuff.h"
  1292. X
  1293. char *rcname INIT(Nullch);        /* path name of .newsrc file */
  1294. char *rctname INIT(Nullch);        /* path name of temp .newsrc file */
  1295. char *rcbname INIT(Nullch);        /* path name of backup .newsrc file */
  1296. char *softname INIT(Nullch);        /* path name of .rnsoft file */
  1297. XFILE *rcfp INIT(Nullfp);        /* .newsrc file pointer */
  1298. X
  1299. static void grow_rc_arrays ANSI((int));
  1300. static void parse_rcline ANSI((NG_NUM));
  1301. X
  1302. X#ifdef HASHNG
  1303. X    static int hashsiz;
  1304. X    static short *hashtbl = NULL;
  1305. X#endif
  1306. X
  1307. bool
  1308. rcstuff_init()
  1309. X{
  1310. X    register NG_NUM newng;
  1311. X    register int i;
  1312. X    register bool foundany = FALSE;
  1313. X    char *some_buf;
  1314. X    long length;
  1315. X#ifdef SERVER
  1316. X    char *cp;
  1317. X#endif /* SERVER */
  1318. X    bool found = FALSE;
  1319. X
  1320. X    /* make filenames */
  1321. X
  1322. X#ifdef SERVER
  1323. X
  1324. X    if (cp = getenv("NEWSRC"))
  1325. X    rcname = savestr(filexp(cp));
  1326. X    else
  1327. X    rcname = savestr(filexp(RCNAME));
  1328. X
  1329. X#else /* not SERVER */
  1330. X
  1331. X    rcname = savestr(filexp(RCNAME));
  1332. X
  1333. X#endif /* SERVER */
  1334. X
  1335. X    rctname = savestr(filexp(RCTNAME));
  1336. X    rcbname = savestr(filexp(RCBNAME));
  1337. X    softname = savestr(filexp(SOFTNAME));
  1338. X    
  1339. X    /* make sure the .newsrc file exists */
  1340. X
  1341. X    newsrc_check();
  1342. X
  1343. X    /* open .rnsoft file containing soft ptrs to active file */
  1344. X
  1345. X    tmpfp = fopen(softname,"r");
  1346. X    if (tmpfp == Nullfp)
  1347. X    writesoft = TRUE;
  1348. X
  1349. X    /* allocate memory for rc file globals */
  1350. X    grow_rc_arrays(1500);
  1351. X
  1352. X    /* read in the .newsrc file */
  1353. X
  1354. X    for (nextrcline = 0;
  1355. X    (some_buf = get_a_line(buf,LBUFLEN,rcfp)) != Nullch;
  1356. X    nextrcline++) {
  1357. X                    /* for each line in .newsrc */
  1358. X    char tmpbuf[10];
  1359. X
  1360. X    newng = nextrcline;        /* get it into a register */
  1361. X    length = len_last_line_got;    /* side effect of get_a_line */
  1362. X    if (length <= 1) {        /* only a newline??? */
  1363. X        nextrcline--;        /* compensate for loop increment */
  1364. X        continue;
  1365. X    }
  1366. X    if (newng >= maxrcline)        /* check for overflow */
  1367. X        grow_rc_arrays(maxrcline + 500);
  1368. X    if (tmpfp != Nullfp && fgets(tmpbuf,10,tmpfp) != Nullch)
  1369. X        softptr[newng] = atol(tmpbuf);
  1370. X    else
  1371. X        softptr[newng] = 0;
  1372. X    some_buf[--length] = '\0';    /* wipe out newline */
  1373. X    if (checkflag)            /* no extra mallocs for -c */
  1374. X        rcline[newng] = some_buf;
  1375. X    else if (some_buf == buf) {
  1376. X        rcline[newng] = savestr(some_buf);
  1377. X                    /* make a semipermanent copy */
  1378. X    }
  1379. X    else {
  1380. X        /*NOSTRICT*/
  1381. X#ifndef lint
  1382. X        some_buf = saferealloc(some_buf,(MEM_SIZE)(length+1));
  1383. X#endif /* lint */
  1384. X        rcline[newng] = some_buf;
  1385. X    }
  1386. X#ifdef NOTDEF
  1387. X    if (strnEQ(some_buf,"to.",3)) {    /* is this a non-newsgroup? */
  1388. X        nextrcline--;        /* destroy this line */
  1389. X        continue;
  1390. X    }
  1391. X#endif
  1392. X    if (*some_buf == ' ' ||
  1393. X      *some_buf == '\t' ||
  1394. X      strnEQ(some_buf,"options",7)) {        /* non-useful line? */
  1395. X        toread[newng] = TR_JUNK;
  1396. X        rcchar[newng] = ' ';
  1397. X        rcnums[newng] = 0;
  1398. X        continue;
  1399. X    }
  1400. X    parse_rcline(newng);
  1401. X    if (rcchar[newng] == NEGCHAR) {
  1402. X        toread[newng] = TR_UNSUB;
  1403. X        continue;
  1404. X    }
  1405. X
  1406. X    /* now find out how much there is to read */
  1407. X
  1408. X    if (!inlist(buf) || (suppress_cn && foundany && !paranoid))
  1409. X        toread[newng] = TR_NONE;    /* no need to calculate now */
  1410. X    else
  1411. X        set_toread(newng);
  1412. X#ifdef VERBOSE
  1413. X    if (!checkflag && softmisses == 1) {
  1414. X        softmisses++;        /* lie a little */
  1415. X        fputs("(Revising soft pointers--be patient.)\n",stdout) FLUSH;
  1416. X    }
  1417. X#endif
  1418. X    if (toread[newng] > TR_NONE) {    /* anything unread? */
  1419. X        if (!foundany) {
  1420. X        starthere = newng;
  1421. X        foundany = TRUE;    /* remember that fact*/
  1422. X        }
  1423. X        if (suppress_cn) {        /* if no listing desired */
  1424. X        if (checkflag) {    /* if that is all they wanted */
  1425. X            finalize(1);    /* then bomb out */
  1426. X        }
  1427. X        }
  1428. X        else {
  1429. X#ifdef VERBOSE
  1430. X        IF(verbose)
  1431. X            printf("Unread news in %-40s %5ld article%s\n",
  1432. X            rcline[newng],(long)toread[newng],
  1433. X            toread[newng]==TR_ONE ? nullstr : "s") FLUSH;
  1434. X        ELSE
  1435. X#endif
  1436. X#ifdef TERSE
  1437. X            printf("%s: %ld article%s\n",
  1438. X            rcline[newng],(long)toread[newng],
  1439. X            toread[newng]==TR_ONE ? nullstr : "s") FLUSH;
  1440. X#endif
  1441. X        if (int_count) {
  1442. X            countdown = 1;
  1443. X            int_count = 0;
  1444. X        }
  1445. X        if (countdown) {
  1446. X            if (! --countdown) {
  1447. X            fputs("etc.\n",stdout) FLUSH;
  1448. X            if (checkflag)
  1449. X                finalize(1);
  1450. X            suppress_cn = TRUE;
  1451. X            }
  1452. X        }
  1453. X        }
  1454. X    }
  1455. X    }
  1456. X    fclose(rcfp);            /* close .newsrc */
  1457. X    if (tmpfp != Nullfp)
  1458. X    fclose(tmpfp);            /* close .rnsoft */
  1459. X    if (checkflag) {            /* were we just checking? */
  1460. X    finalize(foundany);        /* tell them what we found */
  1461. X    }
  1462. X    if (paranoid)
  1463. X    cleanup_rc();
  1464. X
  1465. X#ifdef HASHNG
  1466. X
  1467. X    /* find a good hash size given num of newsgroups */
  1468. X
  1469. X    hashsiz = maxrcline * 1.15;
  1470. X    if ((hashsiz & 1) == 0)    /* must be odd */
  1471. X    hashsiz++;
  1472. X
  1473. X    if (hashsiz <= activeitems)
  1474. X    hashsiz = activeitems * 1.15;
  1475. X    /* find one that's prime */
  1476. X    while (! found) {
  1477. X    for (i=3; ; i+=2) {
  1478. X        if ( (float)(hashsiz)/i == hashsiz/i )
  1479. X        break;
  1480. X        if ( i > hashsiz/i ) {
  1481. X        found = TRUE;
  1482. X        break;
  1483. X        }
  1484. X    }
  1485. X    if (! found)
  1486. X        hashsiz += 2;
  1487. X    }
  1488. X
  1489. X    hashtbl = (short *) safemalloc( hashsiz * sizeof(short) );
  1490. X
  1491. X    for (i=0; i<hashsiz; i++)
  1492. X    hashtbl[i] = -1;
  1493. X
  1494. X    if (!checkflag)
  1495. X    for (i=0; i<nextrcline; i++)
  1496. X        sethash(i);
  1497. X#endif
  1498. X
  1499. X#ifdef DEBUGGING
  1500. X    if (debug & DEB_HASH) {
  1501. X    page_init();
  1502. X    for (i=0; i<hashsiz; i++) {
  1503. X        sprintf(buf,"%d    %d",i,hashtbl[i]);
  1504. X        print_lines(buf,NOMARKING);
  1505. X    }
  1506. X    }
  1507. X#endif
  1508. X
  1509. X    return foundany;
  1510. X}
  1511. X
  1512. static void
  1513. parse_rcline(ngnum)
  1514. NG_NUM ngnum;
  1515. X{
  1516. X#ifdef M_XENIX
  1517. X    char *s;            /* bypass a compiler bug (ugh!) */
  1518. X#else
  1519. X    register char *s;
  1520. X#endif
  1521. X
  1522. X    for (s = rcline[ngnum]; *s && *s != ':' && *s != NEGCHAR; s++) ;
  1523. X    if (!*s && !checkflag) {
  1524. X#ifndef lint
  1525. X    rcline[ngnum] = saferealloc(rcline[ngnum],
  1526. X                (MEM_SIZE)(s - rcline[ngnum]) + 3);
  1527. X#endif /* lint */
  1528. X    strcpy(s, ": ");
  1529. X    }
  1530. X#ifdef USETHREADS
  1531. X    if (*s == ':' && s[1] && s[2] == '0') {
  1532. X    rcchar[ngnum] = '0';
  1533. X    s[2] = '1';
  1534. X    } else
  1535. X#endif
  1536. X    rcchar[ngnum] = *s;    /* salt away the : or ! */
  1537. X    rcnums[ngnum] = (char)(s - rcline[ngnum]) + 1;
  1538. X                /* remember where the numbers are */
  1539. X    *s = '\0';            /* null terminate newsgroup name */
  1540. X}
  1541. X
  1542. void
  1543. abandon_ng(ngnum)
  1544. NG_NUM ngnum;
  1545. X{
  1546. X    char *some_buf = Nullch;
  1547. X
  1548. X    /* open .oldnewsrc and try to find the prior value for the group. */
  1549. X    if ((rcfp = fopen(rcbname, "r")) != Nullfp) {
  1550. X    int length = rcnums[ngnum] - 1;
  1551. X
  1552. X    while ((some_buf = get_a_line(buf,LBUFLEN,rcfp)) != Nullch) {
  1553. X        if (len_last_line_got <= 0)
  1554. X        continue;
  1555. X        some_buf[len_last_line_got-1] = '\0';    /* wipe out newline */
  1556. X        if ((some_buf[length] == ':' || some_buf[length] == NEGCHAR)
  1557. X         && strnEQ(rcline[ngnum], some_buf, length)) {
  1558. X        break;
  1559. X        }
  1560. X        if (some_buf != buf)
  1561. X        free(some_buf);
  1562. X    }
  1563. X    fclose(rcfp);
  1564. X    } else if (errno != ENOENT) {
  1565. X    printf("Unable to open %s.\n", rcbname) FLUSH;
  1566. X    return;
  1567. X    }
  1568. X    if (some_buf == Nullch) {
  1569. X    some_buf = rcline[ngnum] + rcnums[ngnum];
  1570. X    if (*some_buf == ' ')
  1571. X        some_buf++;
  1572. X    *some_buf = '\0';
  1573. X#ifdef CACHEFIRST
  1574. X    abs1st[ngnum] = 0;    /* force group to be re-calculated */
  1575. X#endif
  1576. X    }
  1577. X    else {
  1578. X    free(rcline[ngnum]);
  1579. X    if (some_buf == buf) {
  1580. X        rcline[ngnum] = savestr(some_buf);
  1581. X    }
  1582. X    else {
  1583. X        /*NOSTRICT*/
  1584. X#ifndef lint
  1585. X        some_buf = saferealloc(some_buf, (MEM_SIZE)(len_last_line_got));
  1586. X#endif /* lint */
  1587. X        rcline[ngnum] = some_buf;
  1588. X    }
  1589. X    }
  1590. X    parse_rcline(ngnum);
  1591. X    if (rcchar[ngnum] == NEGCHAR)
  1592. X    rcchar[ngnum] = ':';
  1593. X    set_toread(ngnum);
  1594. X}
  1595. X
  1596. X/* try to find or add an explicitly specified newsgroup */
  1597. X/* returns TRUE if found or added, FALSE if not. */
  1598. X/* assumes that we are chdir'ed to SPOOL */
  1599. X
  1600. X#define ADDNEW_SUB ':'
  1601. X#define ADDNEW_UNSUB '!'
  1602. X
  1603. static int addnewbydefault = 0;
  1604. X
  1605. bool
  1606. get_ng(what,do_reloc)
  1607. char *what;
  1608. bool_int do_reloc;
  1609. X{
  1610. X    char *ntoforget;
  1611. X    char promptbuf[128];
  1612. X    int autosub;
  1613. X
  1614. X#ifdef VERBOSE
  1615. X    IF(verbose)
  1616. X    ntoforget = "Type n to forget about this newsgroup.\n";
  1617. X    ELSE
  1618. X#endif
  1619. X#ifdef TERSE
  1620. X    ntoforget = "n to forget it.\n";
  1621. X#endif
  1622. X    if (index(what,'/')) {
  1623. X    dingaling();
  1624. X    printf("\nBad newsgroup name.\n") FLUSH;
  1625. X    return FALSE;
  1626. X    }
  1627. X    set_ngname(what);
  1628. X    ng = find_ng(ngname);
  1629. X    if (ng == nextrcline) {        /* not in .newsrc? */
  1630. X
  1631. X#ifdef SERVER
  1632. X    sprintf(ser_line, "GROUP %s", ngname);
  1633. X    put_server(ser_line);
  1634. X    if (nntp_get(ser_line, sizeof(ser_line)) < 0) {
  1635. X        fprintf(stderr, "\nrrn: Unexpected close of server socket.\n");
  1636. X        finalize(1);
  1637. X    }
  1638. X    if (*ser_line != CHAR_OK) {
  1639. X        if (atoi(ser_line) != ERR_NOGROUP) {
  1640. X        fprintf(stderr, "\nServer response to GROUP %s:\n%s\n",
  1641. X            ngname, ser_line);
  1642. X        finalize(1);
  1643. X        }
  1644. X#else /* not SERVER */
  1645. X
  1646. X    if (ng >= maxrcline)        /* check for overflow */
  1647. X        grow_rc_arrays(maxrcline + 25);
  1648. X
  1649. X    if ((softptr[ng] = findact(buf,ngname,strlen(ngname),0L)) < 0 ) {
  1650. X
  1651. X#endif /* SERVER */
  1652. X
  1653. X        dingaling();
  1654. X#ifdef VERBOSE
  1655. X        IF(verbose)
  1656. X        printf("\nNewsgroup %s does not exist!\n",ngname) FLUSH;
  1657. X        ELSE
  1658. X#endif
  1659. X#ifdef TERSE
  1660. X        printf("\nNo %s!\n",ngname) FLUSH;
  1661. X#endif
  1662. X        sleep(2);
  1663. X        return FALSE;
  1664. X    }
  1665. X    autosub = auto_subscribe(ngname);
  1666. X    if (!autosub) autosub = addnewbydefault;
  1667. X    if (autosub) {
  1668. X        printf("(Adding %s to end of your .newsrc %ssubscribed)\n",
  1669. X               ngname, (autosub == ADDNEW_SUB) ? "" : "un");
  1670. X        ng = add_newsgroup(ngname, autosub);
  1671. X            do_reloc = FALSE;
  1672. X    } else {
  1673. X#ifdef VERBOSE
  1674. X    IF(verbose)
  1675. X        sprintf(promptbuf,"\nNewsgroup %s not in .newsrc--subscribe? [ynYN] ",ngname);
  1676. X    ELSE
  1677. X#endif
  1678. X#ifdef TERSE
  1679. X        sprintf(promptbuf,"\nSubscribe %s? [ynYN] ",ngname);
  1680. X#endif
  1681. reask_add:
  1682. X    in_char(promptbuf,'A');
  1683. X    setdef(buf,"y");
  1684. X#ifdef VERIFY
  1685. X    printcmd();
  1686. X#endif
  1687. X    putchar('\n') FLUSH;
  1688. X    if (*buf == 'h') {
  1689. X#ifdef VERBOSE
  1690. X        IF(verbose)
  1691. X        printf("Type y or SP to add %s to your .newsrc.\nType Y to add all new groups to the end of your .newsrc.\nType N to add all new groups to the end of your .newsrc unsubscribed.\n", ngname)
  1692. X          FLUSH;
  1693. X        ELSE
  1694. X#endif
  1695. X#ifdef TERSE
  1696. X        fputs("y or SP to add, Y to add all new groups, N to add all new groups unsubscribed\n",stdout) FLUSH;
  1697. X#endif
  1698. X        fputs(ntoforget,stdout) FLUSH;
  1699. X        goto reask_add;
  1700. X    }
  1701. X    else if (*buf == 'n' || *buf == 'q') {
  1702. X        ng = add_newsgroup(ngname, '!');
  1703. X        return FALSE;
  1704. X    }
  1705. X    else if (*buf == 'y') {
  1706. X        ng = add_newsgroup(ngname, ':');
  1707. X        do_reloc = TRUE;
  1708. X    }
  1709. X    else if (*buf == 'Y') {
  1710. X        fputs(
  1711. X    "(I'll add all new newsgroups (subscribed) to the end of your .newsrc.)\n",
  1712. X          stdout);
  1713. X        addnewbydefault = ADDNEW_SUB;
  1714. X        printf("(Adding %s to end of your .newsrc subscribed)\n", ngname);
  1715. X        ng = add_newsgroup(ngname, ':');
  1716. X        do_reloc = FALSE;
  1717. X    }
  1718. X    else if (*buf == 'N') {
  1719. X        fputs(
  1720. X  "(I'll add all new newsgroups (unsubscribed) to the end of your .newsrc.)\n",
  1721. X          stdout);
  1722. X        addnewbydefault = ADDNEW_UNSUB;
  1723. X        printf("(Adding %s to end of your .newsrc unsubscribed)\n", ngname);
  1724. X        ng = add_newsgroup(ngname, '!');
  1725. X        do_reloc = FALSE;
  1726. X    }
  1727. X    else {
  1728. X        fputs(hforhelp,stdout) FLUSH;
  1729. X        settle_down();
  1730. X        goto reask_add;
  1731. X    }
  1732. X      }
  1733. X    }
  1734. X    else if (mode == 'i')        /* adding new groups during init? */
  1735. X    return FALSE;
  1736. X    else if (rcchar[ng] == NEGCHAR) {    /* unsubscribed? */
  1737. X#ifdef VERBOSE
  1738. X    IF(verbose)
  1739. X        sprintf(promptbuf,
  1740. X"\nNewsgroup %s is currently unsubscribed to--resubscribe? [yn] ",ngname)
  1741. X  FLUSH;
  1742. X    ELSE
  1743. X#endif
  1744. X#ifdef TERSE
  1745. X        sprintf(promptbuf,"\n%s unsubscribed--resubscribe? [yn] ",ngname)
  1746. X          FLUSH;
  1747. X#endif
  1748. reask_unsub:
  1749. X    in_char(promptbuf,'R');
  1750. X    setdef(buf,"y");
  1751. X#ifdef VERIFY
  1752. X    printcmd();
  1753. X#endif
  1754. X    putchar('\n') FLUSH;
  1755. X    if (*buf == 'h') {
  1756. X#ifdef VERBOSE
  1757. X        IF(verbose)
  1758. X        printf("Type y or SP to resubscribe to %s.\n", ngname) FLUSH;
  1759. X        ELSE
  1760. X#endif
  1761. X#ifdef TERSE
  1762. X        fputs("y or SP to resubscribe.\n",stdout) FLUSH;
  1763. X#endif
  1764. X        fputs(ntoforget,stdout) FLUSH;
  1765. X        goto reask_unsub;
  1766. X    }
  1767. X    else if (*buf == 'n' || *buf == 'q') {
  1768. X        return FALSE;
  1769. X    }
  1770. X    else if (*buf == 'y') {
  1771. X#ifdef USETHREADS
  1772. X        register char *cp = rcline[ng] + rcnums[ng];
  1773. X        rcchar[ng] = (*cp && cp[1] == '0' ? '0' : ':');
  1774. X#else
  1775. X        rcchar[ng] = ':';
  1776. X#endif
  1777. X        do_reloc = FALSE;
  1778. X    }
  1779. X    else {
  1780. X        fputs(hforhelp,stdout) FLUSH;
  1781. X        settle_down();
  1782. X        goto reask_unsub;
  1783. X    }
  1784. X    }
  1785. X
  1786. X    /* now calculate how many unread articles in newsgroup */
  1787. X
  1788. X    set_toread(ng);
  1789. X#ifdef RELOCATE
  1790. X    if (do_reloc)
  1791. X    ng = relocate_newsgroup(ng,-1);
  1792. X#endif
  1793. X    return toread[ng] >= TR_NONE;
  1794. X}
  1795. X
  1796. X/* add a newsgroup to the .newsrc file (eventually) */
  1797. X
  1798. NG_NUM
  1799. add_newsgroup(ngn, c)
  1800. char *ngn;
  1801. char_int c;
  1802. X{
  1803. X    register NG_NUM newng = nextrcline++;
  1804. X                    /* increment max rcline index */
  1805. X
  1806. X    if (newng >= maxrcline)        /* check for overflow */
  1807. X    grow_rc_arrays(maxrcline + 25);
  1808. X
  1809. X    rcnums[newng] = strlen(ngn) + 1;
  1810. X    rcline[newng] = safemalloc((MEM_SIZE)(rcnums[newng] + 2));
  1811. X    strcpy(rcline[newng],ngn);        /* and copy over the name */
  1812. X    strcpy(rcline[newng]+rcnums[newng], " ");
  1813. X    rcchar[newng] = c;            /* subscribe or unsubscribe */
  1814. X    toread[newng] = TR_NONE;    /* just for prettiness */
  1815. X#ifdef HASHNG
  1816. X    sethash(newng);            /* so we can find it again */
  1817. X#endif
  1818. X    return newng;
  1819. X}
  1820. X
  1821. X#ifdef RELOCATE
  1822. NG_NUM
  1823. relocate_newsgroup(ngx,newng)
  1824. NG_NUM ngx;
  1825. NG_NUM newng;
  1826. X{
  1827. X    char *dflt = (ngx!=current_ng ? "$^.L" : "$^L");
  1828. X    char *tmprcline;
  1829. X    ART_UNREAD tmptoread;
  1830. X    char tmprcchar;
  1831. X    char tmprcnums;
  1832. X    ACT_POS tmpsoftptr;
  1833. X    register NG_NUM i;
  1834. X#if defined(DEBUGGING) || defined(USETHREADS)
  1835. X    ART_NUM tmpngmax;
  1836. X#endif
  1837. X#ifdef CACHEFIRST
  1838. X    ART_NUM tmpabs1st;
  1839. X#endif
  1840. X    
  1841. X    starthere = 0;                      /* Disable this optimization */
  1842. X    writesoft = TRUE;            /* Update soft pointer file */
  1843. X    if (ngx < nextrcline-1) {
  1844. X#ifdef HASHNG
  1845. X    for (i=0; i<hashsiz; i++) {
  1846. X        if (hashtbl[i] > ngx)
  1847. X        --hashtbl[i];
  1848. X        else if (hashtbl[i] == ngx)
  1849. X        hashtbl[i] = nextrcline-1;
  1850. X    }
  1851. X#endif
  1852. X    tmprcline = rcline[ngx];
  1853. X    tmptoread = toread[ngx];
  1854. X    tmprcchar = rcchar[ngx];
  1855. X    tmprcnums = rcnums[ngx];
  1856. X    tmpsoftptr = softptr[ngx];
  1857. X#if defined(DEBUGGING) || defined(USETHREADS)
  1858. X    tmpngmax = ngmax[ngx];
  1859. X#endif
  1860. X#ifdef CACHEFIRST
  1861. X    tmpabs1st = abs1st[ngx];
  1862. X#endif
  1863. X    for (i=ngx+1; i<nextrcline; i++) {
  1864. X        rcline[i-1] = rcline[i];
  1865. X        toread[i-1] = toread[i];
  1866. X        rcchar[i-1] = rcchar[i];
  1867. X        rcnums[i-1] = rcnums[i];
  1868. X        softptr[i-1] = softptr[i];
  1869. X#if defined(DEBUGGING) || defined(USETHREADS)
  1870. X        ngmax[i-1] = ngmax[i];
  1871. X#endif
  1872. X#ifdef CACHEFIRST
  1873. X        abs1st[i-1] = abs1st[i];
  1874. X#endif
  1875. X    }
  1876. X    rcline[nextrcline-1] = tmprcline;
  1877. X    toread[nextrcline-1] = tmptoread;
  1878. X    rcchar[nextrcline-1] = tmprcchar;
  1879. X    rcnums[nextrcline-1] = tmprcnums;
  1880. X    softptr[nextrcline-1] = tmpsoftptr;
  1881. X#if defined(DEBUGGING) || defined(USETHREADS)
  1882. X    ngmax[nextrcline-1] = tmpngmax;
  1883. X#endif
  1884. X#ifdef CACHEFIRST
  1885. X    abs1st[nextrcline-1] = tmpabs1st;
  1886. X#endif
  1887. X    }
  1888. X    if (current_ng > ngx)
  1889. X    current_ng--;
  1890. X    if (newng < 0) {
  1891. X      reask_reloc:
  1892. X    unflush_output();        /* disable any ^O in effect */
  1893. X#ifdef VERBOSE
  1894. X    IF(verbose)
  1895. X        printf("\nPut newsgroup where? [%s] ", dflt);
  1896. X    ELSE
  1897. X#endif
  1898. X#ifdef TERSE
  1899. X        printf("\nPut where? [%s] ", dflt);
  1900. X#endif
  1901. X    fflush(stdout);
  1902. X      reinp_reloc:
  1903. X    eat_typeahead();
  1904. X    getcmd(buf);
  1905. X    if (errno || *buf == '\f') {
  1906. X                /* if return from stop signal */
  1907. X        goto reask_reloc;    /* give them a prompt again */
  1908. X    }
  1909. X    setdef(buf,dflt);
  1910. X#ifdef VERIFY
  1911. X    printcmd();
  1912. X#endif
  1913. X    if (*buf == 'h') {
  1914. X#ifdef VERBOSE
  1915. X        IF(verbose) {
  1916. X        printf("\n\n\
  1917. Type ^ to put the newsgroup first (position 0).\n\
  1918. Type $ to put the newsgroup last (position %d).\n", nextrcline-1);
  1919. X        printf("\
  1920. Type . to put it before the current newsgroup (position %d).\n", current_ng);
  1921. X        printf("\
  1922. Type -newsgroup name to put it before that newsgroup.\n\
  1923. Type +newsgroup name to put it after that newsgroup.\n\
  1924. Type a number between 0 and %d to put it at that position.\n", nextrcline-1);
  1925. X        printf("\
  1926. Type L for a listing of newsgroups and their positions.\n") FLUSH;
  1927. X        }
  1928. X        ELSE
  1929. X#endif
  1930. X#ifdef TERSE
  1931. X        {
  1932. X        printf("\n\n\
  1933. X^ to put newsgroup first (pos 0).\n\
  1934. X$ to put last (pos %d).\n", nextrcline-1);
  1935. X        printf("\
  1936. X. to put before current newsgroup (pos %d).\n", current_ng);
  1937. X        printf("\
  1938. X-newsgroup to put before newsgroup.\n\
  1939. X+newsgroup to put after.\n\
  1940. number in 0-%d to put at that pos.\n", nextrcline-1);
  1941. X        printf("\
  1942. L for list of .newsrc.\n") FLUSH;
  1943. X        }
  1944. X#endif
  1945. X        goto reask_reloc;
  1946. X    }
  1947. X    else if (*buf == 'L') {
  1948. X        putchar('\n') FLUSH;
  1949. X        list_newsgroups();
  1950. X        goto reask_reloc;
  1951. X    }
  1952. X    else if (isdigit(*buf)) {
  1953. X        if (!finish_command(TRUE))    /* get rest of command */
  1954. X        goto reinp_reloc;
  1955. X        newng = atol(buf);
  1956. X        if (newng < 0)
  1957. X        newng = 0;
  1958. X        if (newng >= nextrcline)
  1959. X        return nextrcline-1;
  1960. X    }
  1961. X    else if (*buf == '^') {
  1962. X        putchar('\n') FLUSH;
  1963. X        newng = 0;
  1964. X    }
  1965. X    else if (*buf == '$') {
  1966. X        newng = nextrcline-1;
  1967. X    }
  1968. X    else if (*buf == '.') {
  1969. X        putchar('\n') FLUSH;
  1970. X        newng = current_ng;
  1971. X    }
  1972. X    else if (*buf == '-' || *buf == '+') {
  1973. X        if (!finish_command(TRUE))    /* get rest of command */
  1974. X        goto reinp_reloc;
  1975. X        newng = find_ng(buf+1);
  1976. X        if (newng == nextrcline) {
  1977. X        fputs("Not found.",stdout) FLUSH;
  1978. X        goto reask_reloc;
  1979. X        }
  1980. X        if (*buf == '+')
  1981. X        newng++;
  1982. X    }
  1983. X    else {
  1984. X        printf("\n%s",hforhelp) FLUSH;
  1985. X        settle_down();
  1986. X        goto reask_reloc;
  1987. X    }
  1988. X    }
  1989. X    if (newng < nextrcline-1) {
  1990. X#ifdef HASHNG
  1991. X    for (i=0; i<hashsiz; i++) {
  1992. X        if (hashtbl[i] == nextrcline-1)
  1993. X        hashtbl[i] = newng;
  1994. X        else if (hashtbl[i] >= newng)
  1995. X        ++hashtbl[i];
  1996. X    }
  1997. X#endif
  1998. X    tmprcline = rcline[nextrcline-1];
  1999. X    tmptoread = toread[nextrcline-1];
  2000. X    tmprcchar = rcchar[nextrcline-1];
  2001. X    tmprcnums = rcnums[nextrcline-1];
  2002. X    tmpsoftptr = softptr[nextrcline-1];
  2003. X#if defined(DEBUGGING) || defined(USETHREADS)
  2004. X    tmpngmax = ngmax[nextrcline-1];
  2005. X#endif
  2006. X#ifdef CACHEFIRST
  2007. X    tmpabs1st = abs1st[nextrcline-1];
  2008. X#endif
  2009. X    for (i=nextrcline-2; i>=newng; i--) {
  2010. X        rcline[i+1] = rcline[i];
  2011. X        toread[i+1] = toread[i];
  2012. X        rcchar[i+1] = rcchar[i];
  2013. X        rcnums[i+1] = rcnums[i];
  2014. X        softptr[i+1] = softptr[i];
  2015. X#if defined(DEBUGGING) || defined(USETHREADS)
  2016. X        ngmax[i+1] = ngmax[i];
  2017. X#endif
  2018. X#ifdef CACHEFIRST
  2019. X        abs1st[i+1] = abs1st[i];
  2020. X#endif
  2021. X    }
  2022. X    rcline[newng] = tmprcline;
  2023. X    toread[newng] = tmptoread;
  2024. X    rcchar[newng] = tmprcchar;
  2025. X    rcnums[newng] = tmprcnums;
  2026. X    softptr[newng] = tmpsoftptr;
  2027. X#if defined(DEBUGGING) || defined(USETHREADS)
  2028. X    ngmax[newng] = tmpngmax;
  2029. X#endif
  2030. X#ifdef CACHEFIRST
  2031. X    abs1st[newng] = tmpabs1st;
  2032. X#endif
  2033. X    }
  2034. X    if (current_ng >= newng)
  2035. X    current_ng++;
  2036. X    return newng;
  2037. X}
  2038. X#endif
  2039. X
  2040. X/* List out the newsrc with annotations */
  2041. X
  2042. void
  2043. list_newsgroups()
  2044. X{
  2045. X    register NG_NUM i;
  2046. X    char tmpbuf[2048];
  2047. X    static char *status[] = {"(READ)","(UNSUB)","(BOGUS)","(JUNK)"};
  2048. X    int cmd;
  2049. X
  2050. X    page_init();
  2051. X    print_lines("\
  2052. X  #  Status  Newsgroup\n\
  2053. X",STANDOUT);
  2054. X    for (i=0; i<nextrcline && !int_count; i++) {
  2055. X    if (toread[i] >= 0)
  2056. X        set_toread(i);
  2057. X#ifdef USETHREADS
  2058. X    *(rcline[i] + rcnums[i] - 1) = RCCHAR(rcchar[i]);
  2059. X#else
  2060. X    *(rcline[i] + rcnums[i] - 1) = rcchar[i];
  2061. X#endif
  2062. X    if (toread[i] > 0)
  2063. X        sprintf(tmpbuf,"%3d %6ld   ",i,(long)toread[i]);
  2064. X    else
  2065. X        sprintf(tmpbuf,"%3d %7s  ",i,status[-toread[i]]);
  2066. X    safecpy(tmpbuf+13,rcline[i],2034);
  2067. X    *(rcline[i] + rcnums[i] - 1) = '\0';
  2068. X    if (cmd = print_lines(tmpbuf,NOMARKING)) {
  2069. X        if (cmd > 0)
  2070. X        pushchar(cmd);
  2071. X        break;
  2072. X    }
  2073. X    }
  2074. X    int_count = 0;
  2075. X}
  2076. X
  2077. X/* find a newsgroup in .newsrc */
  2078. X
  2079. NG_NUM
  2080. find_ng(ngnam)
  2081. char *ngnam;
  2082. X{
  2083. X    register NG_NUM ngnum;
  2084. X#ifdef HASHNG
  2085. X    register int hashix = hash(ngnam);
  2086. X    register int incr = 1;
  2087. X
  2088. X    while ((ngnum = hashtbl[hashix]) >= 0) {
  2089. X    if (strEQ(rcline[ngnum], ngnam) && toread[ngnum] >= TR_UNSUB)
  2090. X        return ngnum;
  2091. X    hashix = (hashix + incr) % hashsiz;
  2092. X    incr += 2;            /* offsets from original are in n*2 */
  2093. X    }
  2094. X    return nextrcline;            /* = notfound */
  2095. X
  2096. X#else /* just do linear search */
  2097. X
  2098. X    for (ngnum = 0; ngnum < nextrcline; ngnum++) {
  2099. X    if (strEQ(rcline[ngnum],ngnam))
  2100. X        break;
  2101. X    }
  2102. X    return ngnum;
  2103. X#endif
  2104. X}
  2105. X
  2106. void
  2107. cleanup_rc()
  2108. X{
  2109. X    register NG_NUM ngx;
  2110. X    register NG_NUM bogosity = 0;
  2111. X
  2112. X#ifdef VERBOSE
  2113. X    IF(verbose)
  2114. X    fputs("Checking out your .newsrc--hang on a second...\n",stdout)
  2115. X      FLUSH;
  2116. X    ELSE
  2117. X#endif
  2118. X#ifdef TERSE
  2119. X    fputs("Checking .newsrc--hang on...\n",stdout) FLUSH;
  2120. X#endif
  2121. X    for (ngx = 0; ngx < nextrcline; ngx++) {
  2122. X    if (toread[ngx] >= TR_UNSUB) {
  2123. X        set_toread(ngx);        /* this may reset newsgroup */
  2124. X                    /* or declare it bogus */
  2125. X    }
  2126. X    if (toread[ngx] == TR_BOGUS)
  2127. X        bogosity++;
  2128. X    }
  2129. X    for (ngx = nextrcline-1; ngx >= 0 && toread[ngx] == TR_BOGUS; ngx--)
  2130. X    bogosity--;            /* discount already moved ones */
  2131. X    if (nextrcline > 5 && bogosity > nextrcline / 2) {
  2132. X    fputs(
  2133. X"It looks like the active file is messed up.  Contact your news administrator,\n\
  2134. X",stdout);
  2135. X    fputs(
  2136. X"leave the \"bogus\" groups alone, and they may come back to normal.  Maybe.\n\
  2137. X",stdout) FLUSH;
  2138. X    }
  2139. X#ifdef RELOCATE
  2140. X    else if (bogosity) {
  2141. X#ifdef VERBOSE
  2142. X    IF(verbose)
  2143. X        fputs("Moving bogus newsgroups to the end of your .newsrc.\n",
  2144. X        stdout) FLUSH;
  2145. X    ELSE
  2146. X#endif
  2147. X#ifdef TERSE
  2148. X        fputs("Moving boguses to the end.\n",stdout) FLUSH;
  2149. X#endif
  2150. X    for (; ngx >= 0; ngx--) {
  2151. X        if (toread[ngx] == TR_BOGUS)
  2152. X        relocate_newsgroup(ngx,nextrcline-1);
  2153. X    }
  2154. X#ifdef DELBOGUS
  2155. reask_bogus:
  2156. X    in_char("Delete bogus newsgroups? [ny] ", 'D');
  2157. X    setdef(buf,"n");
  2158. X#ifdef VERIFY
  2159. X    printcmd();
  2160. X#endif
  2161. X    putchar('\n') FLUSH;
  2162. X    if (*buf == 'h') {
  2163. X#ifdef VERBOSE
  2164. X        IF(verbose)
  2165. X        fputs("\
  2166. Type y to delete bogus newsgroups.\n\
  2167. Type n or SP to leave them at the end in case they return.\n\
  2168. X",stdout) FLUSH;
  2169. X        ELSE
  2170. X#endif
  2171. X#ifdef TERSE
  2172. X        fputs("y to delete, n to keep\n",stdout) FLUSH;
  2173. X#endif
  2174. X        goto reask_bogus;
  2175. X    }
  2176. X    else if (*buf == 'n' || *buf == 'q')
  2177. X        ;
  2178. X    else if (*buf == 'y') {
  2179. X        while (toread[nextrcline-1] == TR_BOGUS && nextrcline > 0)
  2180. X        --nextrcline;        /* real tough, huh? */
  2181. X    }
  2182. X    else {
  2183. X        fputs(hforhelp,stdout) FLUSH;
  2184. X        settle_down();
  2185. X        goto reask_bogus;
  2186. X    }
  2187. X#endif
  2188. X    }
  2189. X#else
  2190. X#ifdef VERBOSE
  2191. X    IF(verbose)
  2192. X    fputs("You should edit bogus newsgroups out of your .newsrc.\n",
  2193. X        stdout) FLUSH;
  2194. X    ELSE
  2195. X#endif
  2196. X#ifdef TERSE
  2197. X    fputs("Edit boguses from .newsrc.\n",stdout) FLUSH;
  2198. X#endif
  2199. X#endif
  2200. X    paranoid = FALSE;
  2201. X}
  2202. X
  2203. X#ifdef HASHNG
  2204. X/* make an entry in the hash table for the current newsgroup */
  2205. X
  2206. void
  2207. sethash(thisng)
  2208. NG_NUM thisng;
  2209. X{
  2210. X    register int hashix = hash(rcline[thisng]);
  2211. X    register int incr = 1;
  2212. X#ifdef DEBUGGING
  2213. X    static int hashhits = 0, hashtries = 0;
  2214. X#endif
  2215. X
  2216. X#ifdef DEBUGGING
  2217. X    hashtries++;
  2218. X#endif
  2219. X    while (hashtbl[hashix] >= 0) {
  2220. X#ifdef DEBUGGING
  2221. X    hashhits++;
  2222. X    if (debug & DEB_HASH) {
  2223. X        printf("  Hash hits: %d / %d\n",hashhits, hashtries) FLUSH;
  2224. X    }
  2225. X    hashtries++;
  2226. X#endif
  2227. X    hashix = (hashix + incr) % hashsiz;
  2228. X    incr += 2;            /* offsets from original are in n*2 */
  2229. X    }
  2230. X    hashtbl[hashix] = thisng;
  2231. X}
  2232. X
  2233. short prime[] = {1,2,-3,-5,7,11,-13,-17,19,23,-29,-31,37,41,-43,-47,53,57,-59,
  2234. X    -61,67,71,-73,-79,83,89,-97,-101,1,1,1,1,1,1,1,1,1,1,1,1};
  2235. X
  2236. int
  2237. hash(ngnam)
  2238. register char *ngnam;
  2239. X{
  2240. X    register int i = 0;
  2241. X    register int ch;
  2242. X    register int sum = 0;
  2243. X#ifdef DEBUGGING
  2244. X    char *ngn = ngnam;
  2245. X#endif
  2246. X
  2247. X    while (ch = *ngnam++) {
  2248. X    sum += (ch + i) * prime[i];   /* gives ~ 10% hits at 25% full */
  2249. X    i++;
  2250. X    }
  2251. X#ifdef DEBUGGING
  2252. X    if (debug & DEB_HASH)
  2253. X    printf("hash(%s) => %d => %d\n",ngn, sum, (sum<0?-sum:sum)%hashsiz)
  2254. X      FLUSH;
  2255. X#endif
  2256. X    if (sum < 0)
  2257. X    sum = -sum;
  2258. X    return (sum % hashsiz);
  2259. X}
  2260. X
  2261. X#endif
  2262. X
  2263. void
  2264. newsrc_check()
  2265. X{
  2266. X    rcfp = fopen(rcname,"r");        /* open it */
  2267. X    if (rcfp == Nullfp) {            /* not there? */
  2268. X#ifdef VERBOSE
  2269. X    IF(verbose)
  2270. X        fputs("\nTrying to set up a .newsrc file--running newsetup...\n\n\
  2271. X",stdout) FLUSH;
  2272. X    ELSE
  2273. X#endif
  2274. X#ifdef TERSE
  2275. X        fputs("Setting up .newsrc...\n",stdout) FLUSH;
  2276. X#endif
  2277. X    if (doshell(sh,filexp(NEWSETUP)) ||
  2278. X        (rcfp = fopen(rcname,"r")) == Nullfp) {
  2279. X#ifdef VERBOSE
  2280. X        IF(verbose)
  2281. X        fputs("\nCan't create a .newsrc--you must do it yourself.\n\
  2282. X",stdout) FLUSH;
  2283. X        ELSE
  2284. X#endif
  2285. X#ifdef TERSE
  2286. X        fputs("(Fatal)\n",stdout) FLUSH;
  2287. X#endif
  2288. X        finalize(1);
  2289. X    }
  2290. X    }
  2291. X    else {
  2292. X    UNLINK(rcbname);        /* unlink backup file name */
  2293. X    link(rcname,rcbname);        /* and backup current name */
  2294. X    }
  2295. X}
  2296. X
  2297. X/* write out the (presumably) revised .newsrc */
  2298. X
  2299. void
  2300. write_rc()
  2301. X{
  2302. X    register NG_NUM tmpng;
  2303. X    register char *delim;
  2304. X
  2305. X    rcfp = fopen(rctname, "w");        /* open .newnewsrc */
  2306. X    if (rcfp == Nullfp) {
  2307. X    printf(cantrecreate,".newsrc") FLUSH;
  2308. X    finalize(1);
  2309. X    }
  2310. X    if (stat(rcname,&filestat)>=0) {    /* preserve permissions */
  2311. X    chmod(rctname,filestat.st_mode&0666);
  2312. X    chown(rctname,filestat.st_uid,filestat.st_gid);    /* if possible */
  2313. X    }
  2314. X
  2315. X    /* write out each line*/
  2316. X
  2317. X    for (tmpng = 0; tmpng < nextrcline; tmpng++) {
  2318. X    if (rcnums[tmpng]) {
  2319. X        delim = rcline[tmpng] + rcnums[tmpng] - 1;
  2320. X#ifdef USETHREADS
  2321. X        *delim = RCCHAR(rcchar[tmpng]);
  2322. X        if (rcchar[tmpng] == '0' && delim[2] == '1')
  2323. X        delim[2] = '0';
  2324. X#else
  2325. X        *delim = rcchar[tmpng];
  2326. X#endif
  2327. X    }
  2328. X    else
  2329. X        delim = Nullch;
  2330. X#ifdef DEBUGGING
  2331. X    if (debug & DEB_NEWSRC_LINE)
  2332. X        printf("%s\n",rcline[tmpng]) FLUSH;
  2333. X#endif
  2334. X    if (fprintf(rcfp,"%s\n",rcline[tmpng]) < 0) {
  2335. X    write_error:
  2336. X        printf(cantrecreate,".newsrc") FLUSH;
  2337. X        fclose(rcfp);        /* close .newnewsrc */
  2338. X        UNLINK(rctname);
  2339. X        finalize(1);
  2340. X    }
  2341. X    if (delim) {
  2342. X        *delim = '\0';        /* might still need this line */
  2343. X#ifdef USETHREADS
  2344. X        if (rcchar[tmpng] == '0' && delim[2] == '0')
  2345. X        delim[2] = '1';
  2346. X#endif
  2347. X    }
  2348. X    }
  2349. X    fflush(rcfp);
  2350. X    if (ferror(rcfp))
  2351. X    goto write_error;
  2352. X
  2353. X    fclose(rcfp);            /* close .newnewsrc */
  2354. X    UNLINK(rcname);
  2355. X#ifdef RENAME
  2356. X    rename(rctname,rcname);
  2357. X#else
  2358. X    link(rctname,rcname);
  2359. X    UNLINK(rctname);
  2360. X#endif
  2361. X
  2362. X    if (writesoft) {
  2363. X    tmpfp = fopen(filexp(softname), "w");    /* open .rnsoft */
  2364. X    if (tmpfp == Nullfp) {
  2365. X        printf(cantcreate,filexp(softname)) FLUSH;
  2366. X        return;
  2367. X    }
  2368. X    for (tmpng = 0; tmpng < nextrcline; tmpng++) {
  2369. X        fprintf(tmpfp,"%ld\n",(long)softptr[tmpng]);
  2370. X    }
  2371. X    fclose(tmpfp);
  2372. X    }
  2373. X}
  2374. X
  2375. void
  2376. get_old_rc()
  2377. X{
  2378. X    UNLINK(rctname);
  2379. X#ifdef RENAME
  2380. X    rename(rcname,rctname);
  2381. X    rename(rcbname,rcname);
  2382. X#else
  2383. X    link(rcname,rctname);
  2384. X    UNLINK(rcname);
  2385. X    link(rcbname,rcname);
  2386. X    UNLINK(rcbname);
  2387. X#endif
  2388. X}
  2389. X
  2390. static char *
  2391. grow(ptr, elem, size)
  2392. char *ptr;
  2393. int elem;
  2394. int size;
  2395. X{
  2396. X    if (ptr != NULL)
  2397. X    return saferealloc(ptr, (MEM_SIZE)elem * size);
  2398. X    else
  2399. X    return safemalloc((MEM_SIZE)elem * size);
  2400. X}
  2401. X
  2402. static void
  2403. grow_rc_arrays(newsize)
  2404. int newsize;
  2405. X{
  2406. X#if defined(CACHEFIRST) || defined(USETHREADS)
  2407. X    register int i;
  2408. X#endif
  2409. X
  2410. X#ifdef CACHEFIRST
  2411. X    abs1st = (ART_NUM *) grow((char*)abs1st, newsize, sizeof(ART_NUM));
  2412. X#endif
  2413. X#if defined(DEBUGGING) || defined(USETHREADS)
  2414. X    ngmax = (ART_NUM *) grow((char*)ngmax, newsize, sizeof(ART_NUM));
  2415. X#endif
  2416. X    rcline = (char **) grow((char*)rcline, newsize, sizeof(char*));
  2417. X    toread = (ART_UNREAD *) grow((char*)toread, newsize, sizeof(ART_UNREAD));
  2418. X    rcchar = (char *) grow(rcchar, newsize, sizeof(char));
  2419. X    rcnums = (char *) grow(rcnums, newsize, sizeof(char));
  2420. X    softptr = (ACT_POS *) grow((char*)softptr, newsize, sizeof(ACT_POS));
  2421. X
  2422. X#if defined(CACHEFIRST) || defined(USETHREADS)
  2423. X    for (i=maxrcline; i < newsize; i++) {
  2424. X# ifdef CACHEFIRST
  2425. X    abs1st[i] = 0;
  2426. X# endif
  2427. X# ifdef USETHREADS
  2428. X    ngmax[i] = 0;
  2429. X# endif
  2430. X    }
  2431. X#endif /* CACHEFIRST || USETHREADS */
  2432. X    maxrcline = newsize;
  2433. X    return;
  2434. X}
  2435. END_OF_FILE
  2436. if test 27693 -ne `wc -c <'rcstuff.c'`; then
  2437.     echo shar: \"'rcstuff.c'\" unpacked with wrong size!
  2438. fi
  2439. # end of 'rcstuff.c'
  2440. fi
  2441. echo shar: End of archive 9 \(of 13\).
  2442. cp /dev/null ark9isdone
  2443. MISSING=""
  2444. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do
  2445.     if test ! -f ark${I}isdone ; then
  2446.     MISSING="${MISSING} ${I}"
  2447.     fi
  2448. done
  2449. if test "${MISSING}" = "" ; then
  2450.     echo You have unpacked all 13 archives.
  2451.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2452. else
  2453.     echo You still need to unpack the following archives:
  2454.     echo "        " ${MISSING}
  2455. fi
  2456. ##  End of shell archive.
  2457. exit 0
  2458.